package com.myorigin.weblog.common.aspect;

import com.myorigin.weblog.common.utils.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;


@Aspect
@Component
@Slf4j
public class ApiOperationLogAspect {
    /** 以自定义 @ApiOperationLog 注解为切点，凡是添加 @ApiOperationLog 的方法，都会执行环绕中的代码 */
    @Pointcut("@annotation(com.myorigin.weblog.common.aspect.ApiOperationLog)")
    public void apiOperationLog() {};

    @Around("apiOperationLog()")
    public Object doAround(ProceedingJoinPoint joinPoint)throws Throwable{
        try{
            //请求开始时间
            long starTime = System.currentTimeMillis();
            //MDC
            MDC.put("traceId", UUID.randomUUID().toString());

            //获取类请求的类和方法
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();

            //获取请求入参
            Object[] args = joinPoint.getArgs();
            //入参转 json 字符串
            String argsJsonStr = Arrays.stream(args).map(toJsonStr()).collect(Collectors.joining(","));

            //功能描述信息
            String description = getApiOperationLogDescription(joinPoint);

            //打印相关参数
            log.info("====== 请求开始，[{}]，入参：{}，请求类：{}，请求方法：{} ==================================="
                    , description, argsJsonStr, className, methodName);

            //执行切点方法
            Object result = joinPoint.proceed();

            //执行耗时
            long executiontime = System.currentTimeMillis() - starTime;

            if(description.equals("获取文章详情")){
                //打印出参等相关信息
                log.info("====== 请求结束，[{}]，耗时：{}ms，出参：{} ==================================="
                        , description, executiontime, JsonUtil.toJsonString("文章详情出参略，如需查看请修改切面类进行输出"));
                return result;
            }
            //打印出参等相关信息
            log.info("====== 请求结束，[{}]，耗时：{}ms，出参：{} ==================================="
                    , description, executiontime, JsonUtil.toJsonString(result));

            return result;
        }finally {
            MDC.clear();
        }
    }

    /**
     * 获得注解描述信息
     * @param joinPoint
     * @return
     */
    private String getApiOperationLogDescription(ProceedingJoinPoint joinPoint) {
        //1. 从 ProceedingJoinPoint 获取 MethodSignature
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        //2. 使用 MethodSignature 获取当前被注解的 Method
        Method method = signature.getMethod();

        //3. 从 LogExecution 注解中获取 decription 属性
        ApiOperationLog apiOperationLog = method.getAnnotation(ApiOperationLog.class);

        //4. 从 LogExecution 注解中获取 decription 属性
        return apiOperationLog.description();
    }

    /**
     * 转 JSON 字符串
     * @return
     */
    private Function<Object,String> toJsonStr(){
        return arg -> JsonUtil.toJsonString(arg);
    }
}
